/**
 * @file order_gateway.cpp
 * @brief 订单网关类的实现
 * 
 * @details 该文件实现了订单网关类，负责处理交易系统与交易所之间的订单通信。
 * 实现了将交易引擎生成的客户端请求发送给交易所，
 * 并将交易所返回的客户端响应传递给交易引擎的功能。
 * 
 * @author 原作者
 * @date 2023
 */

#include "order_gateway.h"

namespace Trading {
  /**
   * @brief 构造函数的实现
   * 
   * @details 初始化订单网关对象，设置客户端 ID、请求和响应队列以及网络参数。
   * 创建日志记录器和 TCP 套接字，并设置接收回调函数。
   * 
   * @param client_id 客户端 ID，用于标识当前交易系统实例
   * @param client_requests 客户端请求的无锁队列指针，用于从交易引擎获取请求并发送给交易所
   * @param client_responses 客户端响应的无锁队列指针，用于将交易所的响应传递给交易引擎
   * @param ip 交易所订单服务器的 IP 地址
   * @param iface 网络接口名称
   * @param port 交易所订单服务器的端口号
   */
  OrderGateway::OrderGateway(ClientId client_id,
                             Exchange::ClientRequestLFQueue *client_requests,
                             Exchange::ClientResponseLFQueue *client_responses,
                             std::string ip, const std::string &iface, int port)
      : client_id_(client_id), ip_(ip), iface_(iface), port_(port), outgoing_requests_(client_requests), incoming_responses_(client_responses),
      logger_("trading_order_gateway_" + std::to_string(client_id) + ".log"), tcp_socket_(logger_) {
    tcp_socket_.recv_callback_ = [this](auto socket, auto rx_time) { recvCallback(socket, rx_time); };
  }

  /**
   * @brief 主线程循环函数的实现
   * 
   * @details 该函数实现了订单网关的主要工作循环，负责从客户端请求队列中获取请求，
   * 将其发送给交易所，并接收交易所返回的响应。该函数维护订单的序列号，
   * 确保订单的可靠传输和正确处理。
   * 
   * 函数流程：
   * 1. 记录日志，标记函数开始执行
   * 2. 循环执行以下操作，直到run_标志被设置为false：
   *    a. 调用TCP套接字的sendAndRecv方法，发送和接收数据
   *    b. 从外发请求队列中获取所有待处理的客户端请求
   *    c. 对每个请求，记录性能指标，记录日志，并发送序列号和请求数据
   *    d. 更新请求队列的读取索引，并增加外发序列号
   * 
   * @return void 无返回值
   * 
   * @note 该函数不会抛出异常，使用 noexcept 修饰符来提高性能
   */
  auto OrderGateway::run() noexcept -> void {
    logger_.log("%:% %() %\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str_));
    while (run_) {
      tcp_socket_.sendAndRecv();

      for(auto client_request = outgoing_requests_->getNextToRead(); client_request; client_request = outgoing_requests_->getNextToRead()) {
        TTT_MEASURE(T11_OrderGateway_LFQueue_read, logger_);

        logger_.log("%:% %() % Sending cid:% seq:% %\n", __FILE__, __LINE__, __FUNCTION__,
                    Common::getCurrentTimeStr(&time_str_), client_id_, next_outgoing_seq_num_, client_request->toString());
        START_MEASURE(Trading_TCPSocket_send);
        tcp_socket_.send(&next_outgoing_seq_num_, sizeof(next_outgoing_seq_num_));
        tcp_socket_.send(client_request, sizeof(Exchange::MEClientRequest));
        END_MEASURE(Trading_TCPSocket_send, logger_);
        outgoing_requests_->updateReadIndex();
        TTT_MEASURE(T12_OrderGateway_TCP_write, logger_);

        next_outgoing_seq_num_++;
      }
    }
  }

  /**
   * @brief 接收回调函数的实现
   * 
   * @details 当从交易所接收到客户端响应时调用该回调函数。函数会执行一系列检查，
   * 包括验证客户端ID和序列号的正确性，然后将响应转发到与交易引擎连接的无锁队列中。
   * 
   * 函数流程：
   * 1. 记录性能指标和日志
   * 2. 检查接收缓冲区中是否有足够的数据构成一个完整的客户端响应
   * 3. 如果有足够数据，循环处理每个客户端响应：
   *    a. 将数据转换为客户端响应对象并记录日志
   *    b. 验证客户端 ID 是否正确，如果不正确则记录错误并跳过当前响应
   *    c. 验证序列号是否正确，如果不正确则记录错误并跳过当前响应
   *    d. 增加期望的序列号
   *    e. 将响应写入传入响应队列，并更新写入索引
   * 4. 移动未处理的数据到缓冲区开头，以便于下次处理
   * 5. 记录性能指标
   * 
   * @param socket TCP套接字指针，包含接收到的数据
   * @param rx_time 接收时间戳（纳秒）
   * @return void 无返回值
   * 
   * @note 该函数不会抛出异常，使用 noexcept 修饰符来提高性能
   */
  auto OrderGateway::recvCallback(TCPSocket *socket, Nanos rx_time) noexcept -> void {
    TTT_MEASURE(T7t_OrderGateway_TCP_read, logger_);

    START_MEASURE(Trading_OrderGateway_recvCallback);
    logger_.log("%:% %() % Received socket:% len:% %\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str_), socket->socket_fd_, socket->next_rcv_valid_index_, rx_time);

    if (socket->next_rcv_valid_index_ >= sizeof(Exchange::OMClientResponse)) {
      size_t i = 0;
      for (; i + sizeof(Exchange::OMClientResponse) <= socket->next_rcv_valid_index_; i += sizeof(Exchange::OMClientResponse)) {
        auto response = reinterpret_cast<const Exchange::OMClientResponse *>(socket->inbound_data_.data() + i);
        logger_.log("%:% %() % Received %\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str_), response->toString());

        if(response->me_client_response_.client_id_ != client_id_) { // this should never happen unless there is a bug at the exchange.
          logger_.log("%:% %() % ERROR Incorrect client id. ClientId expected:% received:%.\n", __FILE__, __LINE__, __FUNCTION__,
                      Common::getCurrentTimeStr(&time_str_), client_id_, response->me_client_response_.client_id_);
          continue;
        }
        if(response->seq_num_ != next_exp_seq_num_) { // this should never happen since we use a reliable TCP protocol, unless there is a bug at the exchange.
          logger_.log("%:% %() % ERROR Incorrect sequence number. ClientId:%. SeqNum expected:% received:%.\n", __FILE__, __LINE__, __FUNCTION__,
                      Common::getCurrentTimeStr(&time_str_), client_id_, next_exp_seq_num_, response->seq_num_);
          continue;
        }

        ++next_exp_seq_num_;

        auto next_write = incoming_responses_->getNextToWriteTo();
        *next_write = std::move(response->me_client_response_);
        incoming_responses_->updateWriteIndex();
        TTT_MEASURE(T8t_OrderGateway_LFQueue_write, logger_);
      }
      memcpy(socket->inbound_data_.data(), socket->inbound_data_.data() + i, socket->next_rcv_valid_index_ - i);
      socket->next_rcv_valid_index_ -= i;
    }
    END_MEASURE(Trading_OrderGateway_recvCallback, logger_);
  }
}
